package org.framework.common.api.service.dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.framework.common.api.entity.CommonEntityAware;
import org.framework.common.api.support.model.PageModel;
import org.framework.common.api.support.model.QueryModel;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;

/**
 * <p>描述:公用数据库访问接口的默认实现类</p>
 * <p>Company:上海中信信息发展股份有限公司</p>
 * @author chanlong(陈龙)
 * @date 2013年12月23日  下午2:38:20
 * @version 1.0.2013.
 */
@Repository("defaultDao")
public class CommonDaoDefault<ENTITY extends CommonEntityAware<ID>, ID extends Serializable> extends CommonDaoAdapter<ENTITY, ID> {
	
	@PersistenceContext
	protected EntityManager em;
	
	/**
	 * @see ces.framework.common.web.dao.CommonDaoAdapter#insert(ces.framework.common.entity.CommonEntity)
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日 下午1:27:58
	 */
	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
	public ENTITY insert(final ENTITY entity) throws Exception {
		Assert.notNull(entity, "实体对象不能为空!");
		
		em.persist(entity);
		return entity;
	}

	/**
	 * @see ces.framework.common.web.dao.CommonDaoAdapter#update(ces.framework.common.entity.CommonEntity)
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日 下午1:27:58
	 */
	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
	public ENTITY update(final ENTITY entity) throws Exception {
		Assert.notNull(entity, "实体对象不能为空!");
		
		ENTITY e = em.merge(entity);
		em.flush();
		return e;
	}

	/**
	 * @see ces.framework.common.web.dao.CommonDaoAdapter#remove(ces.framework.common.entity.CommonEntity)
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日 下午1:27:58
	 */
	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
	public void remove(final ENTITY entity) throws Exception {
		Assert.notNull(entity, "实体对象不能为空!");
		
		em.remove(em.contains(entity) ? entity : em.merge(entity));
	}

	/**
	 * @see ces.framework.common.web.dao.CommonDaoAdapter#loader(ces.framework.common.entity.CommonEntity)
	 * @author Administrator(陈龙)
	 * @date 2014-1-22 下午2:05:49
	 */
	@Override
	@SuppressWarnings("unchecked")
	@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
	public ENTITY loader(final ENTITY entity) throws Exception {
		Assert.notNull(entity, "实体对象不能为空!");
		
		return (ENTITY)em.getReference(entity.getClass(), entity.getId());
	}
	
	/**
	 * @see ces.framework.common.web.dao.CommonDaoAdapter#loader(java.lang.Class, java.io.Serializable)
	 * @author chanlong(陈龙)
	 * @date 2013年12月28日 下午1:51:12
	 */
	@Override
	@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
	public ENTITY loader(final Class<ENTITY> cls, final ID id) throws Exception {
		Assert.notNull(id, "主键不能为空!");
		
		return em.getReference(cls, id);
	}
	
	/**
	 * @see org.framework.common.api.service.dao.CommonDaoAdapter#loader(java.lang.Class, java.util.Map)
	 * @author chanlong(陈龙)
	 * @date 2016年4月9日 下午7:06:30
	 */
	@Override
	@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
	public ENTITY loader(final Class<ENTITY> cls, final Map<String,Object> params) throws Exception {
		Assert.notNull(params, "查询参数不能为空!");
		
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<ENTITY> cq = cb.createQuery(cls);
		Root<ENTITY> root = cq.from(cls);
		
		// 构造查询条件
		if(params != null){
			Set<String> keys = params.keySet();
			List<Predicate> ands = new ArrayList<Predicate>(); 
			for (String key : keys) {
				ands.add(cb.equal(root.get(key), params.get(key)));
			}
			cq.where(ands.toArray(new Predicate[]{}));
		}
		
		TypedQuery<ENTITY> query = em.createQuery(cq);
		
		try {
			if (query != null) {
				List<ENTITY> result = query.getResultList();
				if(result != null && !result.isEmpty()) {
					return result.get(0);
				}else{
					return null;
				}
			} else {
				return null;
			}
		} catch (NoResultException e) {
			return null;
		} finally {
			if (params != null) params.clear();
		}
	}
	
	/**
	 * @see ces.framework.common.web.dao.CommonDaoAdapter#loader(java.lang.Class, java.lang.String, java.lang.Object)
	 * @author chanlong(陈龙)
	 * @date 2014年4月25日 下午9:15:59
	 */
	@Override
	@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
	public ENTITY loader(final Class<ENTITY> cls, final String field, final Object value) throws Exception {
		Assert.notNull(field, "字段不能为空!");
		
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<ENTITY> cq = cb.createQuery(cls);
		Root<ENTITY> root = cq.from(cls);
		
		cq.where(cb.equal(root.get(field), value));
		TypedQuery<ENTITY> query = em.createQuery(cq);
		
		try{
			if (query != null) {
				return query.getSingleResult();
			}else{
				return null;
			}
		}catch(NoResultException e){
			return null;
		}
	}
	
	/**
	 * @see org.framework.common.api.service.dao.CommonDaoAdapter#count(org.framework.common.api.support.model.QueryModel)
	 * @author chanlong(陈龙)
	 * @date 2016年4月13日 下午3:22:02
	 */
	@Override
	@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
	public long count(final QueryModel support) throws Exception {
		Class<ENTITY> entityClass = support.getClass4Name();
		CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<Long> query = builder.createQuery(Long.class);
		Root<ENTITY> root = query.from(entityClass);
		
		// 条件
		Predicate condition = createCondition(builder, root, query, support);
		
		// 查询
		query.select(builder.count(root)).where(condition);
		
		// 总数
		return em.createQuery(query).getSingleResult().longValue();
	}
	
	/**
	 * 分页动态条件查询
	 * @see org.framework.common.api.service.dao.CommonDaoAdapter#findByQuery(java.lang.Class, org.framework.common.api.query.QueryBean[], java.lang.Integer, java.lang.Integer)
	 * @author chanlong(陈龙)
	 * @date 2016年1月20日 下午2:39:31
	 */
	@Override
	@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
	public PageModel<ENTITY> findByQuery(final QueryModel support) throws Exception {
		Class<ENTITY> entityClass = support.getClass4Name();
		CriteriaBuilder builder = em.getCriteriaBuilder();
		CriteriaQuery<ENTITY> query = builder.createQuery(entityClass);
		Root<ENTITY> root = query.from(entityClass);
		
		// 条件
		Predicate condition = createCondition(builder, root, query, support);
		
		// 排序
		query.orderBy(createOrder(builder, root, support));
		
		// 分组
		query.groupBy(createGroup(root, support));
		
		// 查询
		List<ENTITY> result = createTypedQuery(em, query.where(condition), support).getResultList();
		
		// 总数
		long count = count(em, builder, entityClass, condition);
		
		// 结果
		return new PageModel<ENTITY>(count, result);
	}
	
	/**
	 * @see ces.framework.common.web.dao.CommonDaoAdapter#findByQuery(java.lang.Class, java.lang.String)
	 * @author chanlong(陈龙)
	 * @date 2014年3月3日 上午11:00:13
	 */
	@Override
	@Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
	public List<ENTITY> findByQuery(final Class<ENTITY> cls, final String jpql, final ModelMap params) throws Exception {
		TypedQuery<ENTITY> query = em.createQuery(jpql, cls);
		return createParams(query, params).getResultList();
	}
}
